package xyz.xiaomeiqiu.learnjvm;

import java.io.*;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;

public class XmqClassLoader extends ClassLoader {

    public static void main(String[] args) {
        List<String> fileNames = Arrays.asList("Hello", "xmq.Hello");
        for (String fileName : fileNames) {
            try {
                printClassMethodFromFile(fileName);
                System.out.println("===== " + fileName + " 执行成功。");
            } catch (Throwable e) {
                System.out.println("===== " + fileName + " 执行失败：" + e.getMessage());
            }
            System.out.println();
        }
    }

    private static void printClassMethodFromFile(String className) throws ClassNotFoundException, InstantiationException, IllegalAccessException, InvocationTargetException {
        Class<?> helloClass = new XmqClassLoader().loadClass(className);
        Object helloInst = helloClass.newInstance();
        for (Method declaredMethod : helloClass.getDeclaredMethods()) {
//            System.out.print("hello class method:" + declaredMethod.getName());
//            System.out.print("\t args method:" + Arrays.toString(declaredMethod.getParameters()));
            if (declaredMethod.getParameters().length == 0) {
                declaredMethod.invoke(helloInst);
            }
        }
    }

    @Override
    protected Class<?> findClass(String name) {
        byte[] fileDatas = null;
        try {
            fileDatas = getClassDataFromFile(name);
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        }
        if (fileDatas != null && fileDatas.length > 0) {
            System.out.println(">>>> 找到 " + name + " 文件并完成byte转换；");
        }
        return defineClass(name, fileDatas, 0, fileDatas.length);
    }

    private String suffix = ".xlass";

    private byte[] getClassDataFromFile(String fileName) throws IOException {
        String fileNameAndPath = fileName.replace(".", File.separator);
        InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream(fileNameAndPath + suffix);
        try (ByteArrayOutputStream bufferedOutputStream = new ByteArrayOutputStream();
             BufferedInputStream byteArrayInputStream = new BufferedInputStream(resourceAsStream)) {
            byte[] buffer = new byte[1024];
            int len = 0;
            while ((len = byteArrayInputStream.read(buffer)) != -1) {
                bufferedOutputStream.write(buffer, 0, len);
            }
            byte[] bytes = bufferedOutputStream.toByteArray();
            return convertByte(bytes);
        }
    }

    private int base_arg = 255;

    private byte[] convertByte(byte[] datas) {
        for (int i = 0; i < datas.length; i++) {
            datas[i] = (byte) (base_arg - datas[i]);
        }
        return datas;
    }

}
